package mk.jug.academy.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import mk.jug.academy.model.Page;
import mk.jug.academy.model.Student;
import mk.jug.academy.model.status.StudentStatus;

import org.springframework.stereotype.Repository;


/**
 * DAO for the {@link Student} entity model. 
 * 
 * @author Ice.Penov
 */
@Repository
public class StudentDAO extends GenericDAO<Student> {
	
	/**
	 * Fetches any student, in a paged-manner, considering the start/end boundaries.
	 */
	public Page<Student> findAll(final int start, final int end) {
		return new QueryEMContext<Page<Student>>() {
			@Override @SuppressWarnings("unchecked")
			public Page<Student> query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s order by s.modified desc");
				query.setFirstResult(start);
				query.setMaxResults(end);
				List<Student> list = query.getResultList();
				query = em.createQuery("Select count(s.id) from Student s");
				return new Page<Student>(initialize(list), (Integer) query.getSingleResult());
			}
		}.result();
	}
				
	/**
	 * Fetches students, with the given status, in a paged-manner, considering the start/end boundaries.
	 */
	public Page<Student> findByStatus(final StudentStatus status, final int start, final int end) {
		return new QueryEMContext<Page<Student>>() {
			@Override @SuppressWarnings("unchecked")
			public Page<Student> query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s where s.status = :status order by s.modified desc");
				query.setParameter("status", status);
				query.setFirstResult(start);
				query.setMaxResults(end);
				List<Student> list = query.getResultList();
				query = em.createQuery("Select count(s.id) from mk.jug.academy.model.Student s where s.status = :status");
				query.setParameter("status", status);
				return new Page<Student>(initialize(list), (Integer) query.getSingleResult());
			}
		}.result();
	}
	
	/**
	 * Fetches students, given the status.
	 */
	public List<Student> findByStatus(final StudentStatus status) {
		return new QueryEMContext<List<Student>>() {
			@Override @SuppressWarnings("unchecked")
			public List<Student> query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s where s.status = :status order by s.modified desc");
				query.setParameter("status", status);
				return initialize(query.getResultList());
			}
		}.result();
	}	
	
	/**
	 * Fetch a list of students, enrolled to a particular course.
	 * 
	 * @param id the course ID
	 */
	public List<Student> findByCourse(final Long id) {
		return new QueryEMContext<List<Student>>() {
			@SuppressWarnings("unchecked")					
			public List<Student> query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s where s.courseID = :courseID");
				query.setParameter("courseID", id);
				List<Student> list = query.getResultList();
				return initialize(list);
			}
		}.result();
	}
	
	/**
	 * Fetch ACTIVE Student with the specified email.
	 */
	public Student findActiveByEmail(final String email) {
		return new QueryEMContext<Student>() {
			@Override
			public Student query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s where s.email = :email and (s.status = :statusWaiting or s.status = :statusEnrolled)");
				query.setParameter("email", email);
				query.setParameter("statusWaiting", StudentStatus.WAITING_FOR_ENROLLMENT);
				query.setParameter("statusEnrolled", StudentStatus.ENROLLED);
				return (Student) query.getSingleResult();
			}
		}.result();
	}

	/**
	 * Fetch ANY Student with the specified email.
	 */
	public Student findAnyByEmail(final String email) {
		return new QueryEMContext<Student>() {
			@Override
			public Student query(EntityManager em) {
				Query query = em.createQuery("Select s from mk.jug.academy.model.Student s where s.email = :email");
				query.setParameter("email", email);
				return (Student) query.getSingleResult();
			}
		}.result();
	}
	
}